/* Copyright 2012 Dustin Mitchell <dmmitche [at] gmail [dot] com>
 *
 * Licensed under the GPLv3.  See included LICENSE.txt for full details.
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <stdio.h>

#ifndef ARM
# include <limits.h>
# include <stdlib.h>
#endif

#include "registers.h"

/* The x86 emulation creates a file in /dev/shm and mmaps this into the
 * process's memory space.  This file is unlinked after a file handle is
 * obtained. */

int registers_map (struct reg_map *mapping, long addr, size_t length)
{
	long page_size;
	off_t offset;
#ifndef ARM
  char shm_file[] = "/dev/shm/beagle-reg-XXXXXX";
#endif

	if (!mapping)
		return -1;

	mapping->dev_mem_fd = -1;
	mapping->mem_mapping = MAP_FAILED;
	mapping->map_length = 0;

#ifdef ARM
	mapping->dev_mem_fd = open ("/dev/mem", O_RDWR);	
#else
  mapping->dev_mem_fd = mkstemp (shm_file);
#endif

	if (mapping->dev_mem_fd == -1)
		return -1;

	page_size = sysconf (_SC_PAGE_SIZE);

#ifdef ARM
	offset = (off_t)((addr / page_size) * page_size);
	mapping->map_offset = (off_t)addr - offset;
#else
  offset = 0;
  mapping->map_offset = 0;

  if (unlink (shm_file) == -1) {
    close (mapping->dev_mem_fd);
    return -1;
  }

  if (ftruncate (mapping->dev_mem_fd, length) == -1) {
    close (mapping->dev_mem_fd);
    return -1;
  }
#endif

	mapping->mem_mapping = mmap (0, length, PROT_READ | PROT_WRITE,
		MAP_SHARED, mapping->dev_mem_fd, offset);
	if (mapping->mem_mapping == MAP_FAILED) {
		close (mapping->dev_mem_fd);
		return -1;
	}

	mapping->map_length = length;

	return 0;
}

void registers_unmap (struct reg_map *mapping)
{
	if (!mapping)
		return;

	if (mapping->mem_mapping != MAP_FAILED)
		munmap (mapping->mem_mapping, mapping->map_length);

	if (mapping->dev_mem_fd != -1)
		close (mapping->dev_mem_fd);

	mapping->dev_mem_fd = -1;
	mapping->mem_mapping = MAP_FAILED;
	mapping->map_length = 0;
}

void registers_map_print (struct reg_map *mapping)
{
	if (!mapping)
		return;

	printf ("fd: %d\nmap: %p\nlength: %ld\noffset: %ld\n",
		mapping->dev_mem_fd, mapping->mem_mapping,
		(long int)mapping->map_length, mapping->map_offset);
}
